package org.lboot.flow.hook;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lboot.flow.constant.FlowWorkStatusConst;
import org.lboot.flow.constant.FlowWorkStatusEnum;
import org.lboot.flow.event.system.*;
import org.lboot.flow.event.user.FlowCancelEvent;
import org.lboot.flow.event.user.FlowCloseEvent;
import org.lboot.flow.event.user.FlowDeleteEvent;
import org.lboot.flow.event.system.FlowStartEvent;
import org.lboot.flow.model.FlowModel;
import org.lboot.flow.model.node.BaseModel;
import org.lboot.flow.module.audit.FlowAudit;
import org.lboot.flow.module.audit.FlowAuditService;
import org.lboot.flow.module.process.FlowProcess;
import org.lboot.flow.module.process.FlowProcessService;
import org.lboot.flow.module.work.FlowWork;
import org.lboot.flow.module.work.FlowWorkService;
import org.lboot.flow.module.work.params.FlowTaskParams;
import org.lboot.flow.parser.FlowNodeParser;
import org.lboot.flow.processor.FlowNodeProcessor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author kindear
 * 流程监听器
 */
@Deprecated
@Slf4j
@Component
@AllArgsConstructor
public class FlowListener {
    // 流程流转事件
    FlowProcessService processService;

    // 节点解析服务列表
    List<FlowNodeProcessor> processors;

    // 流程解析服务
    FlowNodeParser flowNodeParser;

    // 流程审计服务
    FlowAuditService auditService;

    // 流程服务
    FlowWorkService flowWorkService;

    @Resource
    ApplicationContext context;

    /**
     * 监听流程自检事件
     * @param checkEvent
     */
    @SneakyThrows
    @EventListener(FlowCheckEvent.class)
    public void flowCheckEvent(FlowCheckEvent checkEvent){
        FlowWork flowWork = checkEvent.getFlowWork();
        // 检查状态
        if (flowWork.getStatus().equals(0)){
            log.warn("流程实例:[{}:{}]已挂起/结束,无法操作", flowWork.getFlowName(), flowWork.getId());
            return;
        }
        log.info("流程[{}:{}:{}]开始自检",flowWork.getFlowName(),flowWork.getId(),flowWork.getCurrentNodeId());
        // 获取当前节点 -> 解析当前节点类型
        BaseModel baseModel = flowWork.getNodeModel(flowWork.getCurrentNodeId());
        // 是否存在 Processor
        boolean hasProcessor = false;
        // 节点处理器
        for (FlowNodeProcessor processor:processors){
           // log.info(processor.getNodeType());
            if (processor.getNodeType().equals(baseModel.getNodeType())){
                hasProcessor = true;
                // @TODO 自检方法替换
                processor.process(flowWork,checkEvent.getTaskParams());
                break;
            }
        }
        if (!hasProcessor){
            log.error("节点类型:{}不存在Processor", baseModel.getNodeType());
        }
    }

    /**
     * 监听流程启动事件
     * @param startEvent
     */
    @SneakyThrows
    @EventListener(FlowStartEvent.class)
    public void flowStartEvent(FlowStartEvent startEvent){
        FlowWork flowWork = startEvent.getFlowWork();
        log.info("流程[{}:{}]启动",flowWork.getFlowName(),flowWork.getId());
        // 发布流程自检事件
    }

    /**
     * 监听流程启动事件
     * @param event
     * @version 2.0
     */
//    @SneakyThrows
//    @Async("threadExecutor")
//    @Transactional
//    @EventListener(FlowStartEvent.class)
//    public void flowStartEvent(FlowStartEvent event){
//        FlowWork flowWork = event.getFlowWork();
//        flowWorkService.saveOne(flowWork);
//    }

    /**
     * 监听流程执行完毕事件
     * @param endEvent
     */
    @SneakyThrows
    @EventListener(FlowEndEvent.class)
    public void flowEndEvent(FlowEndEvent endEvent){
        FlowWork flowWork = endEvent.getFlowWork();
        log.info("流程[{}:{}]执行完毕",flowWork.getFlowName(),flowWork.getId());
    }
    /**
     * 监听流程实例删除事件
     * @param deleteEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowDeleteEvent.class)
    public void flowDeleteEvent(FlowDeleteEvent deleteEvent){
        FlowWork flowWork = deleteEvent.getFlowWork();
        // 删除对应流程审计信息
        auditService.deleteByFlowWorkId(flowWork.getId());
        // 删除对应流程流转信息
        processService.deleteByFlowWorkId(flowWork.getId());
        // 获取分支流程信息
        if (Validator.isEmpty(flowWork.getRootFlowId())){
            List<FlowWork> subFlows = flowWorkService.getByRootFlowId(flowWork.getId());
            List<String> subFlowIds = subFlows.stream().map(FlowWork::getId).collect(Collectors.toList());
            // 批量删除
            flowWorkService.batchDelete(subFlowIds);
            // 发布事件
            for (FlowWork subFlow:subFlows){
                context.publishEvent(new FlowDeleteEvent(this,subFlow));
            }
        }
    }

    /**
     * 流程实例取消事件
     * @param cancelEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowCancelEvent.class)
    public void flowCancelEvent(FlowCancelEvent cancelEvent){
        FlowWork flowWork = cancelEvent.getFlowWork();
        // 清理流转和审计信息
        processService.deleteByFlowWorkId(flowWork.getId());
        auditService.deleteByFlowWorkId(flowWork.getId());
        // 查询全部子流程
        List<FlowWork> subFlows = flowWorkService.getByRootFlowId(flowWork.getId());
        // 存在子流程 清空关联信息
        if (!subFlows.isEmpty()){
            List<String> subFlowIds = subFlows.stream().map(FlowWork::getId).collect(Collectors.toList());
            // 批量删除
            flowWorkService.batchDelete(subFlowIds);
            // 发布事件
            for (FlowWork subFlow:subFlows){
                context.publishEvent(new FlowDeleteEvent(this,subFlow));
            }
        }

    }


    /**
     * 监听流程流转事件
     * @param processEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowProcessEvent.class)
    public void flowProcessEvent(FlowProcessEvent processEvent){
        // @TODO 下一个节点为用户任务，就要将流程实例置换为等待状态
        FlowWork oldFlow = processEvent.getOldFlow();
        FlowWork newFlow = processEvent.getNewFlow();
        // 构建流程
        FlowProcess process = new FlowProcess();
        // 构建流转信息 条件: currentNode 发生了变化 -> 写入流转记录
        if (!oldFlow.getCurrentNodeId().equals(newFlow.getCurrentNodeId())){
            process.setFromNode(oldFlow.getCurrentNodeId());
            process.setToNode(newFlow.getCurrentNodeId());
            // fix 流转ID 以新流程为准 为适应流程合并 恢复等事件
            process.setFlowWorkId(newFlow.getId());
            // 设置事件
            process.setCreatedTime(DateUtil.date());
            process.setUpdatedTime(DateUtil.date());
            log.info("流程[{}:{}]从节点[{}]流转至节点[{}]",newFlow.getFlowName(),newFlow.getId(),oldFlow.getCurrentNodeId(),newFlow.getCurrentNodeId());
            processService.saveOne(process);
            // 触发流程自检
            context.publishEvent(new FlowCheckEvent(this,newFlow,null));
        }
    }

    /**
     * 监听流程合并事件
     * @param mergeEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowMergeEvent.class)
    public void flowMergeEvent(FlowMergeEvent mergeEvent){
        List<FlowWork> flows = mergeEvent.getFlows();
        FlowWork flowWork = mergeEvent.getFlowWork();
        // 构建流程ID列表
        List<String> flowIds = flows.stream().map(FlowWork::getId).collect(Collectors.toList());
        // 获取全部流程流转信息
        List<FlowProcess> processes = processService.getByFlowIds(flowIds);
        // 更新
        processes = processes.stream().peek(ele-> ele.setFlowWorkId(flowWork.getId())).collect(Collectors.toList());
        processService.batchSave(processes);
        // 获取全部流程审计信息
        List<FlowAudit> audits = auditService.getByFlowIds(flowIds);
        // 更新
        audits = audits.stream().peek(ele -> ele.setFlowWorkId(flowWork.getId())).collect(Collectors.toList());
        auditService.batchSave(audits);
    }

    /**
     * 监听流程fork事件
     * @param forkEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowForkEvent.class)
    public void flowForkEvent(FlowForkEvent forkEvent){
        List<FlowWork> subFlows = forkEvent.getFlows();
        FlowWork flowWork = forkEvent.getFlowWork();
        FlowTaskParams params = forkEvent.getTaskParams();
        // 发布流程流转事件 主流程 -> 分支流程
        for (FlowWork subFlow:subFlows){
            context.publishEvent(new FlowProcessEvent(this,flowWork,subFlow,params));
        }
    }

    /**
     * 监听流程阻塞事件
     * @param blockEvent
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowBlockEvent.class)
    public void flowBlockEvent(FlowBlockEvent blockEvent){
        FlowWork flowWork = blockEvent.getFlowWork();
        log.info("流程[{}:{}]阻塞",flowWork.getFlowName(),flowWork.getId());
    }

    /**
     * 流程关闭事件
     * @param event
     * @version 2.0
     */
    @SneakyThrows
    @Transactional
    @EventListener(FlowCloseEvent.class)
    public void flowCloseEvent(FlowCloseEvent event){
        FlowWork flowWork = event.getFlowWork();
        flowWork.setFlowWorkStatus(FlowWorkStatusEnum.CLOSED.value);
        flowWork.setStatus(0);
        flowWorkService.updateOne(flowWork);
    }



}
